package org.dandsoft.blackbox.laboratory.tools.generator;

import org.dandsoft.blackbox.SignalId;

import java.util.*;
import java.io.Console;
import java.text.MessageFormat;

/**
 * @author Vilyuzhanin Denis (Fin)
 */
public class Function {

    private Map<SignalId, Integer> arguments = new Hashtable<SignalId, Integer>();
    private List<int[]> values = new ArrayList<int[]>();
    private List<Integer> results = new ArrayList<Integer>();

    public Function(SignalId... arguments) {
        int index = 0;
        for (SignalId signal : arguments) {
            this.arguments.put(signal, index);
            index++;
        }
    }

    public Set<SignalId> getArguments() {
        return arguments.keySet();
    }

    public void putValues(int result, Map<SignalId, Integer> values) {
        if (values.size() != arguments.size()) {
            throw new IllegalArgumentException("Wrong arguments count: realSize = " + arguments.size() + ", valuesSize = " + values.size());
        } else if (!isValue(values)) {
            int[] buffer = new int[values.size()];
            for (Map.Entry<SignalId, Integer> valueEntry : values.entrySet()) {
                if (!arguments.containsKey(valueEntry.getKey())) {
                    throw new IllegalArgumentException("SignalId not found: signalId = " + valueEntry.getKey());
                } else {
                    buffer[arguments.get(valueEntry.getKey())] = valueEntry.getValue();
                }
            }
            this.values.add(buffer);
            this.results.add(result);
        }
    }

    public Integer getValue(Map<SignalId, Integer> values) {
        if (values.size() != arguments.size()) {
            throw new IllegalArgumentException("Wrong arguments count: realSize = " + arguments.size() + ", valuesSize = " + values.size());
        }
        int current = 0;
        for (int[] buffer : this.values) {
            boolean isOk = true;
            for (Map.Entry<SignalId, Integer> valueEntry : values.entrySet()) {
                if (arguments.containsKey(valueEntry.getKey())) {
                    int index = arguments.get(valueEntry.getKey());
                    int value = buffer[index];
                    if (value != valueEntry.getValue()) {
                        isOk = false;
                        break;
                    }
                }
            }
            if (isOk) {
                return results.get(current);
            }
            current++;
        }
        return null;
    }

    public boolean isValue(Map<SignalId, Integer> values) {
        return getValue(values) != null;
    }

    public void printFunction() {
        SignalId[] signals = new SignalId[arguments.size()];

        for (Map.Entry<SignalId, Integer> signalEntry : arguments.entrySet()) {
            signals[signalEntry.getValue()] = signalEntry.getKey();
        }

        System.out.print("| ");
        for (int i = 0; i < signals.length; i++) {
            System.out.print("id:" + format(Long.toString(signals[i].getValue()), 3) + " | ");
        }
        System.out.println();


        int resutlIndex = -1;
        for (int[] value : values) {
            resutlIndex++;
            System.out.print("| ");
            for (int i = 0; i < value.length; i++) {
                System.out.print(format(Integer.toString(value[i]), 6) + " | ");
            }
            System.out.println("| " + format(Integer.toString(results.get(resutlIndex)), 6));
        }
        System.out.println();
    }

    private String format(String str, int width) {
        StringBuffer result = new StringBuffer();
        result.append(str);
        if (str.length() < width) {
            for (int i = 0; i < width - str.length(); i++) {
                result.append(" ");
            }
        }

        return result.toString();
    }

    public static Function generateRandom(int sequence, int argumentsCount, double power) {
        SignalId[] signals = new SignalId[argumentsCount];
        Function function;
        for (int i = 0; i < argumentsCount; i++) {
            signals[i] = new SignalId(sequence + i);
        }
        function = new Function(signals);

        int resultsCount = (int) Math.floor((power * Math.pow(2, argumentsCount)));
        int resultValue = 1;
        for (int i = 0; i < resultsCount; i++) {
            Map<SignalId, Integer> values = new Hashtable<SignalId, Integer>();
            boolean isNotNullValue = false;
            for (SignalId signal : signals) {
                int value;
                if (Math.random() * 2 < 1) {
                    value = 0;
                } else {
                    isNotNullValue = true;
                    value = 1;
                }
                values.put(signal, value);
            }
            if (function.isValue(values)) {
                i--;
                continue;
            }
            if (!isNotNullValue) {
                values.put(signals[0], 1);
            }
            function.putValues(resultValue, values);
            resultValue++;

        }
        return function;
    }


}
